/*
 * $Id: MultiPartFileDownloadController.java,v 1.15 2009/11/02 15:00:24 afaichun Exp $
 * 
 * Copyright (C) 2008 General Electric Company. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of the General
 * Electric Company (GE). You shall not disclose this software and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with GE.
 * 
 * GE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. GE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING, OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */
package com.ge.healthcare.autosc.onwatch.controller;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.servlet.ModelAndView;

import com.ge.healthcare.autosc.common.ASCLogger;
import com.ge.healthcare.autosc.common.database.pojo.HstSystemContactInfo;
import com.ge.healthcare.autosc.common.dbLogger.ContactLogger;
import com.ge.healthcare.autosc.onwatch.bean.FileDownloadBean;
import com.ge.healthcare.autosc.onwatch.exception.OnWatchException;
import com.ge.healthcare.autosc.onwatch.pojo.ErrorCode;
import com.ge.healthcare.autosc.onwatch.util.OnWatchCodeConstants;
import com.ge.healthcare.autosc.onwatch.util.OnWatchConstants;
import com.ge.healthcare.autosc.onwatch.util.ResourceBundleUtil;
/**
 * MultiPartFileDownloadController is the HTTP controller for file download request.
 * Requested file will be downloaded through HTTP response stream to the remote system.
 * Controller will redirect to Error View for error condition.  
 *
 * @author 212042946
 *
 */
public class MultiPartFileDownloadController extends AbstractFormController {
	
	//private final static String BINARY_FILE_MIME_TYPE = "application/octet-stream";
	private final static String BINARY_FILE_MIME_TYPE = "multipart/digest";
	private final static String CONTENT_DISPOSITION = "Content-Disposition";
	private final static String READ_ONLY = "r";
	
	/**
	 * Init method (Spring init method)
	 */
	public void init() {
		super.init();
	}
	
	/**
	 * Override onSubmit method from superclass
	 */
	protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response,
			Object command, BindException errors) throws ServletException, IOException {
		String methodName = "onSubmit";
		ASCLogger.debug(this.getClass(), methodName, "On Submit called...");
		ContactLogger.startLogging(request.getRemoteAddr(), HstSystemContactInfo.HTTP_CONNECT_TYPE);
		ContactLogger.addContactLog("File download request");
		FileDownloadBean fileDownloadBean = (FileDownloadBean)command;
		ASCLogger.info(this.getClass(), methodName, "Process request\n" + fileDownloadBean);
		fileDownloadBean.setRemoteAddr(request.getRemoteAddr());
		checkSimulatorRequest(fileDownloadBean);

		ModelAndView mav = null;
		FileInputStream fis = null;
		RandomAccessFile raf = null;
		try {
			File requestFile = getOnWatchService().getDownloadFile(fileDownloadBean);
			if(requestFile.exists()) {
				ServletOutputStream out = response.getOutputStream();
				raf = new RandomAccessFile(requestFile, READ_ONLY);
				//fis = new FileInputStream(requestFile);
				ServletContext context = request.getSession().getServletContext();
				String mimeType = context.getMimeType(requestFile.getName());
				response.setContentType((mimeType != null)?mimeType:BINARY_FILE_MIME_TYPE);
				response.setHeader(CONTENT_DISPOSITION, 
						"attachment; filename=\"" + requestFile.getName() + "\"");
				int contentLength = (int)requestFile.length();
				ASCLogger.info("File size is " + contentLength);
				int offset = fileDownloadBean.getOffsetAsInt();
				ASCLogger.info("Request offset is " + offset);
				if(offset > 0) {
					if(offset >= contentLength) {
						//Request is invalid
						throw new OnWatchException("Offset is invalid", OnWatchCodeConstants.PRC_F_OWR_035);
					}
					contentLength = contentLength - offset;
					raf.seek(offset);
				}
				ASCLogger.info("Amount of data will be read: " + contentLength);
				response.setContentLength(contentLength);

				byte[] buf = new byte[contentLength];
				int length;
				//while((length = fis.read(buf, offset, contentLength)) != -1) {
				while((length = raf.read(buf)) != -1) {
					ASCLogger.info("number of bytes being read from file " + length);
					out.write(buf, 0, length);
				}
				
			} else {
				ErrorCode errorCode = new ErrorCode(this.getClass().getName());
				errorCode.addCode("file.notFound");
				errorCode.addArgument(fileDownloadBean.getFileName());
				errors.addError(errorCode.getObjectError(
						"File not found: " + fileDownloadBean.getFileName()));
				mav = callError(request, response, errors);
			}
		} catch (OnWatchException owe) {
			ASCLogger.debug(this.getClass(), methodName, "get OnWatchException");
			ErrorCode errorCode = new ErrorCode(this.getClass().getName());
			errorCode.addCode("failed.download.detail");
			errorCode.AddErrorCodeAsArgument(getCodeDetailsDAO(), owe.getErrorCode());
			errors.addError(errorCode.getObjectError("File download failed"));
			mav = callError(request, response, errors);
		} catch (Throwable e) {
			ASCLogger.debug(this.getClass(), methodName, "get throwable ", e);
			ErrorCode errorCode = new ErrorCode(this.getClass().getName());
			errorCode.addCode("error.system.internal");
			errors.addError(errorCode.getObjectError("Internal System error"));
			mav = callError(request, response, errors);
		} finally {
			if(fis != null) {
				fis.close();
			}
			
			if(raf != null) {
				raf.close();
			}
			ContactLogger.endLogging(null);
		}
		return mav;
	}
	
	/**
	 * Override showForm method from superclass
	 */
	protected ModelAndView showForm(HttpServletRequest request, HttpServletResponse response,
			BindException errors) throws Exception {
		String methodName = "showForm";
		ASCLogger.debug(this.getClass(), methodName, "In showForm....");
		if(errors.hasErrors()) {
			super.logErrors(errors);
			Map<Object, Object> model = new HashMap<Object, Object>();
			/*
			 *  If multiple errors, this comes from validation or binding.
			 *  Only give remote system the first failed error when remote
			 *  system suppose to know how to communicate.
			 */
			ObjectError err = (ObjectError)errors.getAllErrors().get(0);
			String errorMessage = ResourceBundleUtil.getMessage(
										OnWatchConstants.ONWATCH_MESSAGE_BASENAME, 
										err.getCode(), 
										err.getArguments(), 
										err.getDefaultMessage(), 
										null);

			ContactLogger.addContactLog("Error message: " + errorMessage);
			model.put("error", errorMessage);
			model.put("systemId", request.getParameter("systemId"));
			model.put("iipVersion", request.getParameter("iipVersion"));
			return new ModelAndView("errorView", model);
		}
		return null;
	}
}
